﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BeanboxSoftware.BeanMap;

namespace BeanboxSoftware.BeanMapTest.Unboxed
{
  [TestClass]
  public class DictionaryTest
  {
    private Map<int, int, int, int> _map;
    private IDictionary<int, IDictionary<int, IDictionary<int, int>>> _dic;

    [TestInitialize]
    public void SetUp()
    {
      _map = new Map<int, int, int, int>();
      _dic = _map as IDictionary<int, IDictionary<int, IDictionary<int, int>>>;
    }

    [TestMethod]
    public void IsDiciontaryTest()
    {
      Assert.IsNotNull(_dic);
    }

    [TestMethod]
    public void IsReadOnlyTest()
    {
      //as a collection, it _is_ read only
      Assert.IsFalse(_dic.IsReadOnly);
    }

    [TestMethod]
    public void KeysTest()
    {
      Assert.AreEqual(0, _dic.Keys.Count);

      _map[1, 2, 3] = 4;
      Assert.AreEqual(1, _dic.Keys.Count);
      Assert.AreEqual(1, _dic.Keys.First());

      _map.Clear();
      _map[2, 3, 4] = 1;
      Assert.AreEqual(1, _dic.Keys.Count);
      Assert.AreEqual(2, _dic.Keys.First());
    }

    [TestMethod]
    public void ForEachTest()
    {
      foreach (var kvp in _dic)
      {
        Assert.Fail("unexpected kvp");
      }

      _map[1, 2, 3] = 4;

      foreach (var kvp in _dic)
      {
        Assert.AreEqual(1, kvp.Key);
        Assert.AreEqual(4, kvp.Value[2][3]);
      }
    }

    [TestMethod]
    public void ForEachSecurityTest()
    {
      _map[1, 2, 3] = 4;
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.First().Value[0] = new Dictionary<int, int>(); });

      Assert.AreEqual(4, _dic.First().Value[2][3]);
      _dic.First().Value[2][3] = 5;
      Assert.AreEqual(5, _map[1, 2, 3]);

      _dic.First().Value[4][6] = 8;
      Assert.AreEqual(8, _map[1, 4, 6]);

      _map.DefaultValue = -1;
      Assert.AreEqual(-1, _dic.First().Value[5][100]);
    }

    [TestMethod]
    public void AddNotSupportedTest()
    {
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.Add(1, new Dictionary<int, IDictionary<int, int>>()); });
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.Add(new KeyValuePair<int, IDictionary<int, IDictionary<int, int>>>()); });
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic[1] = new Dictionary<int, IDictionary<int, int>>(); });
    }

    [TestMethod]
    public void GetTest()
    {
      _map[1, 2, 3] = 4;
      Assert.AreEqual(4, _dic[1][2][3]);
      _dic[1][2][3] = 5;
      Assert.AreEqual(5, _dic[1][2][3]);
      Assert.AreEqual(5, _map[1, 2, 3]);
    }

    [TestMethod]
    public void TryGetTest()
    {
      IDictionary<int, IDictionary<int, int>> child;
      Assert.IsTrue(_map.TryGetValue(14, out child));
      Assert.IsNotNull(child);
      child[1][3] = 4;
      Assert.AreEqual(4, _map[14, 1, 3]);
      Assert.AreEqual(4, _dic[14][1][3]);

      _map.DefaultValue = -1;
      Assert.AreEqual(-1, child[10][1000]);
    }

    [TestMethod]
    public void ContainsKVPTest()
    {
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.Contains(new KeyValuePair<int, IDictionary<int, IDictionary<int, int>>>()); });
    }

    [TestMethod]
    public void CopyToTest()
    {
      var buffer = new KeyValuePair<int, IDictionary<int, IDictionary<int, int>>>[4];
      _map[1, 2, 3] = 4;
      _map[2, 2, 3] = 2;
      _map[3, 9, 3] = 3;

      _dic.CopyTo(buffer, 1);

      var one = buffer.First(e => e.Key == 1);
      var two = buffer.First(e => e.Key == 2);
      var three = buffer.First(e => e.Key == 3);

      Assert.AreEqual(4, one.Value[2][3]);
      Assert.AreEqual(2, two.Value[2][3]);
      Assert.AreEqual(3, three.Value[9][3]);
    }

    [TestMethod]
    public void CopyToNullTest()
    {
      TestUtils.AssertExceptionThrown<ArgumentNullException>(() => { _dic.CopyTo(null, 0); });
    }

    [TestMethod]
    public void CopyToOutOfRangeTest()
    {
      var buffer = new KeyValuePair<int, IDictionary<int, IDictionary<int, int>>>[4];
      TestUtils.AssertExceptionThrown<ArgumentOutOfRangeException>(() => { _dic.CopyTo(buffer, -1); });
      TestUtils.AssertExceptionThrown<ArgumentOutOfRangeException>(() => { _dic.CopyTo(buffer, buffer.Length + 1); });
      TestUtils.AssertExceptionThrown<ArgumentOutOfRangeException>(() => { _dic.CopyTo(buffer, buffer.Length + 100); });

      _map[1, 1, 1] = 4;
      _map[2, 2, 2] = 6;
      TestUtils.AssertExceptionThrown<ArgumentOutOfRangeException>(() => { _dic.CopyTo(buffer, buffer.Length - 1); });

      _map.Clear(); // no values should be okay
      _dic.CopyTo(buffer, buffer.Length);

      _map[1, 1, 1] = 1;
      _map[1, 2, 2] = 2;
      _map[2, 2, 2] = 3;
      _map[2, 3, 3] = 4; // only 2 to copy
      _dic.CopyTo(buffer, buffer.Length - 2);
      Assert.AreEqual(4, buffer.First(e => e.Key == 2 && e.Value.ContainsKey(3)).Value[3][3]);
    }

    [TestMethod]
    public void CopyToSecurityTest()
    {
      var buffer = new KeyValuePair<int, IDictionary<int, IDictionary<int, int>>>[3];
      _map[1, 2, 3] = 4;
      _map[2, 2, 3] = 2;

      _dic.CopyTo(buffer, 1);

      var one = buffer.First(e => e.Key == 1);
      var two = buffer.First(e => e.Key == 2);

      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { one.Value[101] = new Dictionary<int, int>(); });

      _map.DefaultValue = -1;
      Assert.AreEqual(-1, two.Value[909][707]);
    }

    [TestMethod]
    public void RemoveKVPTest()
    {
      TestUtils.AssertExceptionThrown<NotSupportedException>(() => { _dic.Remove(new KeyValuePair<int, IDictionary<int, IDictionary<int, int>>>()); });
    }

    [TestMethod]
    public void ValuesTest()
    {
      Assert.AreEqual(0, _dic.Count);

      _map[1, 2, 3] = 7;
      _map[2, 2, 3] = 7;
      _map[3, 2, 3] = 7;
      _map[4, 2, 3] = 7;

      var values = _dic.Values;
      Assert.AreEqual(4, values.Count);

      foreach (var v in values)
      {
        Assert.AreEqual(7, v[2][3]);
      }
    }
  }
}
